/// 声明宏
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test() {
        start();
    }
}

// 这个宏有一个巨大的缺陷
// 目前该宏还有一个问题，就是在调用的时候，最后一个键值对加上逗号时，编译就会出错 。
// 之前最后的键值对没有逗号的时候，匹配模式匹配完该键值对就会正常结束，但是现在加了逗号，
// 当匹配模式匹配完逗号之后，就会继续匹配星号，从而激活重复匹配，但此时后续己经没有键值对供其匹配了。
// 所以会报错，编译器宣告该宏意外结束
macro_rules! hashmap_01 {
    ($($key:expr => $value:expr),*) => (
        {
            let mut _map = ::std::collections::HashMap::new();
            $(
                _map.insert($key, $value);
            )*
            _map
        };
    )
}

// 第一种就是利用宏的递归调用，将最后一行的逗号消去
macro_rules! hashmap_02 {
      //  多次匹配 1 => 1, hashmap_02!("a" => 1, "b" => 2)hashmap_02!("a" => 1, "b" => 2)，
     ($($key:expr => $value:expr,)*) => {   //
        hashmap_02!($($key => $value),*)
     };
     // 多次匹配 1 => 1
     ($($key:expr => $value:expr),*) => {
        {
            let mut _map = ::std::collections::HashMap::new();
            $(
                _map.insert($key, $value);
            )*
            _map
        };
     };
}

// 另外一种方法更简单了，只需要利用重复匹配的技巧即可
macro_rules! hashmap_03 {
    ($($key:expr => $value:expr),* $(,)*) => {
        {
            let mut _map = ::std::collections::HashMap::new();
            $(
                _map.insert($key, $value);
            )*
            _map
        };
    }
}

// 预分配容量
macro_rules! unit {
    ($($x:tt)*) => {
        ()
    };
}

macro_rules! count {
    ($($key:expr),*) => {<[()]>::len(&[$(unit!($key)),*])};
}

macro_rules! hashmap_04 {
    ($($key:expr => $value:expr),* $(,)*) => {
        {
            let _cap = count!($($key),*);
            let mut _map = ::std::collections::HashMap::with_capacity(_cap);
            $(
                _map.insert($key, $value);
            )*
            _map
        };
    }
}

// 虽然完成了预定的目标 ， 但是引入了另外两个宏。这就导致目标宏 hashmap!依赖于两个独立的宏，
// 如果以后想把 hashmap!宏放到独立的包( crate)中对外公开， 那依赖的这两个独立的宏也必须公开，
// 但是这两个宏对外部来说， 基本没有其他作用 。 如何 解决这个问题呢?答案很简单，只需要把依赖的两个宏转移到
// hashmap!宏定义内部作为内部 规则即可
macro_rules! hashmap_05 {
    (@unit $($x:tt)*) => {()};
    (@count $($key:expr),*) => { <[()]>::len(&[$(hashmap_05!(@unit! $key)),*])};
    ($($key:expr => $value:expr),* $(,)*) => {
        {
            let _cap = hashmap_05!(@count $($key),*);
            let mut _map = ::std::collections::HashMap::with_capacity(_cap);
            $(
                _map.insert($key, $value);
            )*
            _map
        };
    }
}

pub fn start() {
    // 使用了#![feature(trace_macros)]属性，注意此时必须使用 Nightly 版本 的 Rust 才能编译 。
    // 在代码 trace_macros!宏调用的上方，加上 trace_macros! (true)就 可以调试宏展开过程。
    // trace_macros!(true);
    let map_01 = hashmap_01!(1 => 1, 2 => 2);
    println!("{:?}", map_01);

    let map_02 = hashmap_02!(1 => 1, 2 => 2,);
    println!("{:?}--{}", map_02, map_02.capacity());

    let map_03 = hashmap_03!(1 => 1, 2 => 2,);
    println!("{:?}--{}", map_03, map_03.capacity());

    let map_04 = hashmap_04!(1 => 1, 2 => 2,);
    println!("{:?}--{}", map_04, map_04.capacity());

    let map_05 = hashmap_05!(1 => 1, 2 => 2,);
    println!("{:?}--{}", map_05, map_05.capacity());
    trace_macros!(false);
}
